;
(function ($) {
    'use strict';

    if (!String.locale) {
        var lang = $('html').attr('lang');
        if (lang) {
            String.locale = lang;
        } else {
            var nav = window.navigator;
            String.locale = nav.language || nav.userLanguage || '';
        }
    }

    function log() {
        if (window.console) {
            window.console.log.apply(window.console, arguments);
        }
    }

    function jsonMessageLoader(url) {
        var deferred = $.Deferred();

        $.getJSON(url).done(deferred.resolve).fail(function (jqxhr, settings, exception) {
            log('Error in loading messages from ' + url + ' Exception: ' + exception);
            deferred.resolve();
        });

        return deferred.promise();
    }

    var MessageStore = function () {
        this.messages = {};
    };

    MessageStore.prototype = {
        load: function (source, locale) {
            var key,
                deferred,
                deferreds = [],
                messageStore = this;

            if (typeof source === 'string') {
                log('Loading messages from: ' + source);
                deferred = jsonMessageLoader(source).done(function (localization) {
                    messageStore.set(locale, localization);
                });

                return deferred.promise();
            } else {
                if (locale) {
                    messageStore.set(locale, source);

                    return $.Deferred().resolve();
                } else {
                    for (key in source) {
                        if (Object.prototype.hasOwnProperty.call(source, key)) {
                            locale = key;
                            deferreds.push(messageStore.load(source[key], locale));
                        }
                    }
                    return $.when.apply($, deferreds);
                }
            }
        },

        set: function (locale, messages) {
            if (!this.messages[locale]) {
                this.messages[locale] = messages;
            } else {
                this.messages[locale] = $.extend(this.messages[locale], messages);
            }
        },

        get: function (locale, messageKey) {
            return this.messages[locale] && this.messages[locale][messageKey];
        }
    };


    function I18N(options) {
        this.options = $.extend({}, I18N.defaults, options);
        this.parser = this.options.parser;
        this.locale = this.options.locale;
        this.messageStore = this.options.messageStore;
        this.init();
    };

    I18N.defaults = {
        locale: String.locale,
        parser: {
            parse: function (message, parameters) {
                return message.replace(/\$(\d+)/g, function (str, match) {
                    var index = parseInt(match, 10) - 1;
                    return parameters[index] !== undefined ? parameters[index] : str;
                });
            }
        },
        messageStore: new MessageStore()
    };

    I18N.prototype = {
        init: function () {
            var i18n = this;

            String.locale = i18n.locale;
        },

        destroy: function () {
            $.removeData(document, 'i18n');
        },

        load: function (source, locale) {
            if (!source && !locale) {
                source = 'i18n/' + this.locale + '.json';
                locale = this.locale;
            }

            if (typeof source === 'string' && source.split('.').pop() !== 'json') {
                var sourceMap = {
                    locale: source + '/' + locale + '.json'
                };
                return this.messageStore(sourceMap);
            } else {
                return this.messageStore.load(source, locale);
            }
        },

        parse: function (key, parameters) {
            var message = this.messageStore.get(this.locale, key);

            if (message === '') {
                message = key;
            }
            return this.parser.parse(message, parameters);
        }
    };

    $.i18n = function (key, param1) {
        var parameters,
            i18n = $.data(document, 'i18n'),
            options = typeof key === 'object' && key;

        if (options) {
            if (i18n) {
                if (options.locale && i18n.locale !== options.locale) {
                    String.locale = i18n.locale = options.locale;
                }
            } else {
                i18n = new I18N(options);
                $.data(document, 'i18n', i18n);
            }
            return i18n;
        } else {
            if (!i18n) {
                i18n = new I18N();
                $.data(document, 'i18n', i18n);
            }

            if (param1 !== undefined) {
                parameters = Array.prototype.slice.call(arguments, 1);
            } else {
                parameters = [];
            }
            if (typeof key === 'string') {
                return i18n.parse(key, parameters);
            } else {
                return i18n;
            }
        }
    };

    $.i18n.constructor = I18N;

    $.fn.i18n = function () {
        var i18n = $.data(document, 'i18n');

        if (!i18n) {
            i18n = new I18N();
            $.data(document, 'i18n', i18n);
        }

        return this.each(function () {
            var $this = $(this),
                messageKey = $this.data('i18n');

            if (messageKey) {
                $this.text(i18n.parse(messageKey));
            } else {
                $this.find('[data-i18n]').i18n();
            }
        });
    };

}($));